#include "thread.h"
#include "../kernel/memory.h"
#include "../lib/kernel/string.h"
#include "../kernel/interrupt.h"

#define GENEREL_TICKS 30

static struct tasks_list ready_task_list, all_task_list;//List of PCB of threads.
static struct task_struct * main_thread_ts = NULL;
static struct task_struct head;

static void thread_exec(thread_func * func, void * args)
{
	enable_intr();
	func(args);
}

static void list_append(struct tasks_list * tl, struct task_struct *ts)
{
	ASSERT(ts != NULL);
	ts->prev = tl->tail;
	ts->next = NULL;
	tl->tail->next = ts;
	tl->tail = ts;
}

static struct task_struct * list_pop(struct tasks_list * tl)
{
	ASSERT(tl->head->next != NULL);
	struct task_struct * ts;
	ts = tl->head->next;
	tl->head->next = tl->head->next->next;
	return ts;
}

static void task_init(struct task_struct * ts, int prio, const char * name)
{
	ASSERT(ts != NULL);
	memset(ts, 0, sizeof(struct task_struct));
	ts->kstack = (uint32_t *)((void *)ts + PG_SIZE);//Point to top of pcb.

	if(ts == main_thread_ts)
	{
		ts->status = RUNNING;//Change task status to running.	
	}
	else
	{
		ts->status = READY;
	}
	list_append(&all_task_list, ts);
	ts->priority = prio;
	strcpy(ts->name , name);
	ts->ticks = prio;
	ts->elapsed_ticks = 0;
	ts->kstack_magic=0x95076400;
}

static void thread_create(struct task_struct * ts, thread_func * func, void * args)
{
	ASSERT(ts != NULL && func != NULL);
	
	ts->kstack = (uint32_t *)((void *)ts->kstack - sizeof(struct intr_stack) -sizeof(struct thread_stack));//Reserve space for interrupt stack.
	//Not reserve space for interrupt stack.
	//Set up thread stack.
	struct thread_stack * ths = (struct thread_stack *)ts->kstack;
	ths->eip = thread_exec;
	ths->function = func;
	print_string("Thread function address is ");
	print_int((uint32_t)ths->function);
	print_string("\r\n");
	ths->args = args;
	ths->ebp = ths->ebx = ths->esi = ths->edi = 0;
}

static inline void tasks_list_init()
{
	ready_task_list.head = ready_task_list.tail = &head;
	all_task_list.head = all_task_list.tail = &head; 
}

void main_thread()
{
	tasks_list_init();
	//uint32_t * esp;
	GET_TASK_STRUCT(main_thread_ts);
	main_thread_ts = (struct task_struct *)((uint32_t)main_thread_ts & 0xfffff000);
	ASSERT( main_thread_ts != NULL);
	print_string("Main thread pcb is ");
	print_int((uint32_t)main_thread_ts);
	print_string("\r\n");
	task_init(main_thread_ts, 30, "main_thread");
}

struct task_struct * run_thread(const char * name, int prio, thread_func * func, void * args)
{
	uint32_t * pcb = malloc_pages(kernel, 1);
	struct task_struct * ts = (struct task_struct *)pcb;
	task_init(ts, prio, name);
	thread_create(ts, func, args);//Put function and its arguments into thread stack.

	//Obtain current thread task_struct.
	struct task_struct * current_ts;
	GET_TASK_STRUCT(current_ts);
	current_ts = (struct task_struct *)((uint32_t)current_ts & 0xfffff000);
	ASSERT( current_ts != NULL);
	current_ts->status = READY;
	list_append(&ready_task_list, current_ts);//Put current list to ready list.
	print_string("Ready list head is ");
	print_int((uint32_t)ready_task_list.head);
	print_string("\r\n");
	ts->status = RUNNING;
	switch_task(ts, current_ts);
	return ts;

}

void schedule()
{
	struct task_struct * current_ts;

	//Assert interrupt is closed.
	diable_intr();
	GET_TASK_STRUCT(current_ts);
	current_ts = (struct task_struct *)((uint32_t)current_ts & 0xfffff000);
	if(current_ts->status == RUNNING)
	{
		current_ts->status = READY;
		list_append(&ready_task_list, current_ts);
		current_ts->ticks = current_ts->priority;
	}	
	else
	{
		//Undefined.
	}
	ASSERT(ready_task_list.head != NULL);
	struct task_struct * next_ts = list_pop(&ready_task_list);
	next_ts->status = RUNNING;
	switch_task(next_ts, current_ts);

}